
namespace apeng {


	let t1 = 0.0

	/**曲线 */
	export class Curve {

		/**贝塞尔 */
		public static readonly Bezier = new class {

			/**一维算法 */
			public readonly vec1 = new class {
				/**
				 * n-1阶贝塞尔
				 * @public	static
				 * @memberof Bezier
				 */
				public gets(values: number[], ratio: number): number {
					let l = values.length
					t1 = 1 - ratio
					let value = 0

					for (let i = 0; i < l; i++) {
						let n = l - i - 1
						value += values[i] * Math.pow(t1, n) * Math.pow(ratio, i) * (this.erxiangshi(l - 1, i))
					}
					return value
				}


				/**
				 * 处理后半段值的缩放，
				 * 作用是 比率由大慢慢趋近于1
				 * @private
				 * @public	static
				 * @param {number} start
				 * @param {number} end
				 * @returns {number}
				 * @memberof Bezier
				 */
				public erxiangshi(start: number, end: number): number {
					let cs = 1, bcs = 1
					while (end > 0) {
						cs *= start
						bcs *= end
						start--
						end--
					}
					return (cs / bcs)
				}


				/**
				 * 二阶
				 * 公式算法 获取曲线上的点
				 * @param end1 第一个端点
				 * @param end2 第二个端点
				 * @param control 控制点
				 * @param time 0~1 运行的速率
				 */
				public getControlByCurve(start: number, end: number, control: number, ratio: number) {
					// 倒数
					t1 = 1 - ratio
					// 二阶贝塞尔公式
					return t1 * t1 * start + 2 * ratio * t1 * control + ratio * ratio * end
				}

				/**
				 * 公式 通过曲线上的点获取控制点
				 * 二阶
				 * @param end1 
				 * @param end2 
				 * @param curve 
				 */
				public getCurveByControl(start: number, end: number, curve: number, ratio: number) {
					t1 = 1 - ratio
					return (curve - t1 * t1 * start - ratio * ratio * end) / (2 * ratio * t1)
				}

				/**
				 * 三阶
				 * 公式算法 获取曲线上的点
				 * @param end1 第一个端点
				 * @param end2 第二个端点
				 * @param control 控制点
				 * @param control2 控制点2
				 * @param time 0~1 运行的速率
				 */
				public getControlByCurve3(end1: number, end2: number, control: number, control2: number, time: number) {
					if (end1 == 0 && end2 == 0)
						return 0
					// 倒数
					t1 = 1 - time
					// 三阶贝塞尔公式
					return Math.pow(t1, 3) * end1 + 3 * control * time * t1 * t1 + 3 * control2 * time * time * t1 + end2 * Math.pow(time, 3)
				}

				/**
				 * 三阶
				 * 公式 通过曲线上的点获取第一个控制点
				 * @param end1 
				 * @param end2 
				 * @param curve 
				 * @param control2 第二个控制点
				 */
				public getCurveByControl3(end1: number, end2: number, curve: number, control2: number, time: number): number {
					t1 = 1 - time
					return (curve - Math.pow(t1, 3) * end1 - 3 * t1 * time * time * control2 - end2 * Math.pow(time, 3)) / (3 * t1 * t1)
				}


				/**
				 * 三阶
				 * 公式 通过曲线上的点获取第二个控制点
				 * @param end1 
				 * @param end2 
				 * @param curve 
				 * @param control 第一个控制点
				 */
				public get3CurveByControl2(end1: number, end2: number, curve: number, control1: number, time: number): number {
					t1 = 1 - time
					return (curve - Math.pow(t1, 3) * end1 - 3 * t1 * t1 * control1 * time - end2 * Math.pow(time, 3)) / (3 * Math.pow(t1, 3) * time * time)
				}
			}

			/**二维 */
			public readonly vec2 = new class {

				public gets(out: IVector2, values: IVector2[], ratio: number): IVector2 {
					let l = values.length
					t1 = 1 - ratio

					for (let i = 0; i < l; i++) {
						let n = l - i - 1;
						let res = Math.pow(t1, n) * Math.pow(ratio, i) * (Curve.Bezier.vec1.erxiangshi(l - 1, i))
						out.x += values[i].x * res
						out.y += values[i].y * res
					}
					return out
				}

				public getControlByCurve<T extends IVector2>(
					start: T,
					end: T,
					control: T,
					ratio: number,
					curve: T
				): T {
					curve.x = Curve.Bezier.vec1.getControlByCurve(start.x, end.x, control.x, ratio)
					curve.y = Curve.Bezier.vec1.getControlByCurve(start.y, end.y, control.y, ratio)
					return curve
				}

				public getCurveByControl<T extends IVector2>(
					start: T,
					end: T,
					curve: T,
					time: number,
					control: T
				): T {
					control.x = Curve.Bezier.vec1.getCurveByControl(start.x, end.x, curve.x, time)
					control.y = Curve.Bezier.vec1.getCurveByControl(start.y, end.y, curve.y, time)
					return control
				}

				/**3阶贝塞尔 */
				public getControlByCurve3<T extends IVector2>(
					start: T,
					end: T,
					control: T,
					control2: T,
					ratio: number,
					curve: T
				): T {
					curve.x = Curve.Bezier.vec1.getControlByCurve3(start.x, end.x, control.x, control2.x, ratio)
					curve.y = Curve.Bezier.vec1.getControlByCurve3(start.y, end.y, control.y, control2.y, ratio)
					return curve
				}

				public getCurveByControl3<T extends IVector2>(
					start: T,
					end: T,
					curve: T,
					control2: T,
					time: number,
					control: T
				): T {
					control.x = Curve.Bezier.vec1.getCurveByControl3(start.x, end.x, curve.x, control2.x, time)
					control.y = Curve.Bezier.vec1.getCurveByControl3(start.y, end.y, curve.y, control2.y, time)
					return control
				}

			}

			/**三维 */
			public readonly vec3 = new class {
				public gets(out: IVector3, values: IVector3[], ratio: number): IVector3 {
					let l = values.length
					t1 = 1 - ratio

					for (let i = 0; i < l; i++) {
						let n = l - i - 1;
						let res = Math.pow(t1, n) * Math.pow(ratio, i) * (Curve.Bezier.vec1.erxiangshi(l - 1, i))
						out.x += values[i].x * res
						out.y += values[i].y * res
						out.z += values[i].z * res
					}
					return out
				}

				public getControlByCurve<T extends IVector3>(
					start: T,
					end: T,
					control: T,
					ratio: number,
					curve: T
				): T {
					curve.x = Curve.Bezier.vec1.getControlByCurve(start.x, end.x, control.x, ratio)
					curve.y = Curve.Bezier.vec1.getControlByCurve(start.y, end.y, control.y, ratio)
					curve.z = Curve.Bezier.vec1.getControlByCurve(start.z, end.z, control.z, ratio)
					return curve
				}

				public getCurveByControl<T extends IVector3>(
					start: T,
					end: T,
					curve: T,
					time: number,
					control: T
				): T {
					control.x = Curve.Bezier.vec1.getCurveByControl(start.x, end.x, curve.x, time)
					control.y = Curve.Bezier.vec1.getCurveByControl(start.y, end.y, curve.y, time)
					control.z = Curve.Bezier.vec1.getCurveByControl(start.z, end.z, curve.z, time)
					return control
				}

				/**3阶贝塞尔 */
				public getControlByCurve3<T extends IVector3>(
					start: T,
					end: T,
					control: T,
					control2: T,
					ratio: number,
					curve: T
				): T {
					curve.x = Curve.Bezier.vec1.getControlByCurve3(start.x, end.x, control.x, control2.x, ratio)
					curve.y = Curve.Bezier.vec1.getControlByCurve3(start.y, end.y, control.y, control2.y, ratio)
					curve.z = Curve.Bezier.vec1.getControlByCurve3(start.z, end.z, control.z, control2.z, ratio)
					return curve
				}

				public getCurveByControl3<T extends IVector3>(
					start: T,
					end: T,
					curve: T,
					control2: T,
					time: number,
					control: T
				): T {
					control.x = Curve.Bezier.vec1.getCurveByControl3(start.x, end.x, curve.x, control2.x, time)
					control.y = Curve.Bezier.vec1.getCurveByControl3(start.y, end.y, curve.y, control2.y, time)
					control.z = Curve.Bezier.vec1.getCurveByControl3(start.z, end.z, curve.z, control2.z, time)
					return control
				}

			}
		}

		/**样条 */
		public static readonly Spline = new class {

			/**一维算法 */
			public readonly vec1 = new class {
				/**
				 * 公式算法 需4个点
				 * @param p0 
				 * @param p1 
				 * @param p2 
				 * @param p3 
				 * @param tension 弹性弯曲弧度 0（正常）， 小于0（趋紧于直线）
				 * @param ratio 0～1比率
				 * @returns 
				 */
				public get(p0: number, p1: number, p2: number, p3: number, tension: number, ratio: number): number {
					let p = -1
					if (ratio == 1)
						p = 3
					else {
						let locDT = 1 / 3
						p = Math.floor(ratio / locDT)
						ratio = (ratio - locDT * p) / locDT
					}

					let arr = [p0, p1, p2, p3]
					p0 = Sets.getAt(arr, p - 1)
					p1 = Sets.getAt(arr, p - 0)
					p2 = Sets.getAt(arr, p + 1)
					p3 = Sets.getAt(arr, p + 2)

					let t2 = ratio * ratio
					let t3 = t2 * ratio
					let s = (1 - tension) / 2

					let b1 = s * ((-t3 + (2 * t2)) - ratio)
					let b2 = s * (-t3 + t2) + (2 * t3 - 3 * t2 + 1)
					let b3 = s * (t3 - 2 * t2 + ratio) + (-2 * t3 + 3 * t2)
					let b4 = s * (t3 - t2)

					return p0 * b1 + p1 * b2 + p2 * b3 + p3 * b4
				}
			}

			public readonly vec2 = new class {
				public get<T extends IVector2>(p0: T, p1: T, p2: T, p3: T, tension: number, ratio: number, out: T): T {
					out.x = Curve.Spline.vec1.get(p0.x, p1.x, p2.x, p3.x, tension, ratio)
					out.y = Curve.Spline.vec1.get(p0.y, p1.y, p2.y, p3.y, tension, ratio)
					return out
				}
			}

			public readonly vec3 = new class {
				public get<T extends IVector3>(p0: T, p1: T, p2: T, p3: T, tension: number, ratio: number, out: T): T {
					out.x = Curve.Spline.vec1.get(p0.x, p1.x, p2.x, p3.x, tension, ratio)
					out.y = Curve.Spline.vec1.get(p0.y, p1.y, p2.y, p3.y, tension, ratio)
					out.z = Curve.Spline.vec1.get(p0.z, p1.z, p2.z, p3.z, tension, ratio)
					return out
				}
			}
		}


	}
}
